import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
from sklearn.svm import SVC
from sklearn.ensemble import VotingClassifier
from sklearn.ensemble import AdaBoostClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import roc_curve, auc
from sklearn import preprocessing
from sklearn.decomposition import PCA
from collections import Counter
from imblearn.over_sampling import SMOTE
import xgboost as xgb
from xgboost import XGBClassifier

data_path = '../data/data.csv'
result_path = '../data/result.csv'
random_seed = 100


def init_options():
    """
    初始化
    :return:
    """
    pd.set_option('display.width', 120)  # pandas设置显示宽度
    pd.set_option('precision', 1)  # 设置显示数值的精度
    pd.set_option('display.max_columns', None)  # 设置显示最大列数
    pd.set_option('display.max_rows', None)  # 设置显示最大行数

    plt.rcParams['font.sans-serif'] = ['SimHei']  # 解决中文显示问题-设置字体为黑体
    plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题
    plt.rcParams['figure.figsize'] = (10, 10)
    sns.set(font='SimHei')  # 解决Seaborn中文显示问题


def preprocess():
    """
    数据预处理，并将处理结果转换为csv格式
    :return: None
    """
    df = pd.read_csv(data_path)
    df['B7'].fillna(0, inplace=True)  # 填充年龄
    df.rename(columns={'目标客户编号': 'id', '品牌类型': 'brand', "购买意愿": 'buy'}, inplace=True)
    df['B8'] = 2020 - df['B8']  # 将出生年份转为年龄
    df.drop('id', axis=1, inplace=True)  # 删除id列，对结果无作用
    cols = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8']
    for col in cols:
        col_mean = df[col].mean()
        q3 = df[col].quantile(q=0.75)  # 上四分卫
        q1 = df[col].quantile(q=0.25)  # 下四分卫
        iqr = q3 - q1
        top = q3 + 1.5 * iqr
        bottom = q1 - 1.5 * iqr
        df[col] = df[col].apply(lambda x: col_mean if (x > top or x < bottom) else x)

    df = df.applymap("{0:.02f}".format)  # 转成两位小数格式
    print(df.describe())

    # print(df.describe())
    # df.to_csv(result_path, index=None)


def analyze_satisfaction():
    """
    分析满意度
    :return:
    """
    df = pd.read_csv(result_path)
    # df = df[df['buy'] == 1]
    df = df.groupby('brand').mean().reset_index()
    df = df.iloc[:, :9]

    # print(df.head())
    # print(df.describe())

    labels = np.array(["电池技术性能", "舒适性", "经济性", "安全性", "动力性", "驾驶操控性", "外观内饰", "配置与质量品质"])
    angles = np.linspace(0, 2 * np.pi, len(labels), endpoint=False)
    data0 = df.loc[0].drop('brand')
    data1 = df.loc[1].drop('brand')
    data2 = df.loc[2].drop('brand')

    plt.style.use('ggplot')

    labels = np.concatenate((labels, [labels[0]]))
    angles = np.concatenate((angles, [angles[0]]))  # 增加第一个angle到所有angle里，以实现闭合
    data0 = np.concatenate((data0, [data0[0]]))
    data1 = np.concatenate((data1, [data1[0]]))
    data2 = np.concatenate((data2, [data2[0]]))

    fig = plt.figure(figsize=(8, 8))
    # fig.subplots_adjust(wspace=0.4)  # 设置子图间的间距，为子图宽度的40%

    ax1 = fig.add_subplot(111, polar=True)
    ax1.set_thetagrids(angles * 180 / np.pi, labels)  # 设置网格标签
    ax1.set_theta_zero_location('NW')  # 设置极坐标0°位置
    ax1.set_rlim(74, 80)  # 设置显示的极径范围
    ax1.plot(angles, data0, color='r')
    ax1.plot(angles, data1, color='g')
    ax1.plot(angles, data2, color='b')
    # ax1.fill(angles, data0, facecolor='r', alpha=0.25)  # 填充颜色
    ax1.set_title("满意度分析图", fontproperties="SimHei", fontsize=24, weight='bold')  # 设置标题
    ax1.legend(["品牌0", "品牌1", "品牌2"], loc=(0.8, 0.95), labelspacing=0.1, fontsize=16)
    # plt.show()
    plt.savefig('satisfaction.jpg')


def analyze_correlation():
    """
    分析相关性
    :return: None
    """
    df = pd.read_csv(result_path)
    fig = plt.figure(figsize=(64, 36))
    # fig.subplots_adjust(wspace=0.4)  # 设置子图间的间距，为子图宽度的40%
    ax_index = 1
    for i in [1, 2, 3]:
        brand_data = df[df['brand'] == i]
        ax = fig.add_subplot(2, 3, ax_index)
        ax.set_title(f"品牌{i}汽车满意度相关性分析", fontproperties="SimHei", fontsize=40, weight='bold')
        data = brand_data[['buy', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8']]
        corrmat = data.corr(method='pearson')
        sns.heatmap(corrmat, annot=True, ax=ax)

        ax = fig.add_subplot(2, 3, ax_index + 3)
        ax.set_title(f"品牌{i}个人特征相关性分析", fontproperties="SimHei", fontsize=40, weight='bold')
        data = brand_data[['buy', 'B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9',
                           'B10', 'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17']]
        corrmat = data.corr(method='spearman')
        sns.heatmap(corrmat, annot=True, ax=ax)
        ax_index += 1

    fig.savefig('../img/correlation.jpg')
    # plt.show()


def analyze_discrete_point():
    """
    分析离群点
    :return:
    """
    df = pd.read_csv(result_path)
    # data = df
    data = df.iloc[:, 1:9]

    # data0 = df.iloc[:, 0]
    # data0.plot(style='go')

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.set_title("异常值分析图", fontproperties="SimHei", fontsize=24, weight='bold')  # 设置标题
    plot = data.boxplot(return_type='dict', boxprops={'color': 'blue'})
    ax.set_xticklabels(["电池", "舒适性", "经济性", "安全性", "动力性", "操控性", "外观", "品质"])
    # x = plot['fliers'][0].get_xdata()  # fliers为异常值标签，get_xdata()与get_ydata()用来获取横纵坐标数组
    # y = plot['fliers'][0].get_ydata()
    # y.sort()
    #
    # for i in range(len(x)):
    #     if i > 0:
    #         plt.annotate(y[i], xy=(x[i], y[i]), xytext=(x[i] + 0.05 - 0.8 / (y[i] - y[i - 1]), y[i]))
    #     else:
    #         plt.annotate(y[i], xy=(x[i], y[i]), xytext=(x[i] + 0.08, y[i]))
    plt.show()


def analyze_feature():
    """
    分析个人特征
    :return:
    """
    df = pd.read_csv(result_path)
    # df = df[df['buy'] == 0]

    cols = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9',
            'B10', 'B11', 'B12', 'B13', 'B14', 'B15', 'B16', 'B17']
    ticklabels = [['老家', '本城市', '其它'], [], ['市中心', '非市中心的城区', '城乡结合部', '县城', '乡镇中心地带', '农村'], [], [],
                  ['未婚, 单独居住', '未婚，与父母同住', '已婚/同居无子女(两人世界)', '已婚/同居无子女（与父母同住）',
                   '已婚，有小孩，不与父母同住', '已婚，有小孩，与父母同住', '离异/丧偶', '其他'], [], [],
                  ['未受过正式教育', '小学', '初中', '高中/中专/技校', '大专', '本科', '双学位/研究生及以上'], [],
                  ['机关单位', '科研/教育/文化/卫生/医疗等事业单位', '国有企业', '私营/民营企业', '外资企业', '合资企业', '个体户/小型公司', '自由职业者', '不工作'],
                  ['高层管理/企业主/老板', '中层管理者', '资深技术人员/高级技术人员', '中级技术人员', '初级技术人员', '资深职员/办事员',
                   '中级职员/办事员', '初级职员/办事员', '个体户/小型公司业主', '自由职业者', '其它'],
                  [], [], [], [], []]
    ticks = [[1, 2, 3], range(0, 60, 10), [1, 2, 3, 4, 5, 6], range(0, 35, 5), range(0, 8, 1), [1, 2, 3, 4, 5, 6, 7, 8],
             range(0, 4, 1), [], [1, 2, 3, 4, 5, 6, 8], [], [1, 2, 3, 4, 5, 6, 7, 8, 9],
             [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [], [], [], range(0, 40, 5), range(0, 40, 5)]
    labels = ['户口情况', '在本城市居住年限', '居住区域', '驾龄', '家里有几口人', '婚姻家庭情况', '孩子数量', '年龄', '最高学历',
              '工作年限', '单位性质', '职位', '家庭年收入', '个人年收入', '家庭可支配年收入', '房贷占年收入比例', '车贷占年收入比例']

    fig = plt.figure(figsize=(48, 36))
    fig.subplots_adjust(wspace=0.4)  # 设置子图间的间距，为子图宽度的40%
    fig.suptitle(t="客户购买的相关因素分析", fontproperties="SimHei", fontsize=48, weight='bold', y=0.95)

    for index, col in enumerate(cols):
        ax = fig.add_subplot(4, 5, index + 1)
        sns.violinplot(x='brand', y=col, hue='buy', data=df, ax=ax, split=True)
        ax.set_title(labels[index], fontproperties="SimHei", fontsize=24, weight='bold')
        ax.set_xticks(ticks=[0, 1, 2])
        ax.set_xticklabels(labels=['品牌1', '品牌2', '品牌3'])
        tick = ticks[index]
        ticklabel = ticklabels[index]
        if len(tick) > 0:
            ax.set_yticks(ticks=tick)
        if len(ticklabel) > 0:
            ax.set_yticklabels(labels=ticklabels[index])
        ax.set_xlabel('', fontproperties="SimHei", fontsize=16, weight='bold')
        ax.set_ylabel('', fontproperties="SimHei", fontsize=16, weight='bold')
        leg_handles = ax.get_legend_handles_labels()[0]
        ax.legend(leg_handles, ['未购买', '购买'], title='是否购买')
    plt.savefig('feature.jpg')
    # plt.show()


def analyze_buy_rate():
    """
    分析购买比率
    :return:
    """
    df = pd.read_csv(result_path)
    buy_rate = []
    for i in [1, 2, 3]:
        data = df[df['brand'] == i]
        data = data[['buy']]
        buy = data[data['buy'] == 1].count()
        notbuy = data[data['buy'] == 0].count()
        buy_rate.append(round(buy / notbuy, 2))
    print(buy_rate)


def plot_roc_auc(fpr, tpr, roc_auc):
    """
    绘制ROC_AUC图
    :return:
    """
    plt.figure(figsize=(10, 10))
    plt.plot(fpr, tpr, color='darkorange',
             lw=2, label='ROC curve (area = %0.2f)' % roc_auc)  ###假正率为横坐标，真正率为纵坐标做曲线
    plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('FP Rate')
    plt.ylabel('TP Rate')
    plt.title('Receiver operating characteristic example')
    plt.legend(loc="lower right")
    plt.show()


def train():
    df = pd.read_csv(result_path)
    # brand = 0
    # df = df[df['brand'] == brand]
    x = df.iloc[:, 1:-1]
    y = df['buy']
    print(Counter(y))
    smo = SMOTE(random_state=random_seed)
    x, y = smo.fit_resample(x, y)  # 数据增强
    print(Counter(y))

    # continuous_cols = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'B2', 'B4', 'B8',
    #                    'B10', 'B13', 'B14', 'B15', 'B16', 'B17']
    # discrete_cols = ['B1', 'B3', 'B5', 'B6', 'B7', 'B9', 'B11', 'B12', ]
    # normalizer = preprocessing.Normalizer()
    # norm_result = normalizer.fit_transform(x[continuous_cols])
    # onehot_encoder = OneHotEncoder(sparse=False)
    # onehot_result = onehot_encoder.fit_transform(x[discrete_cols])
    # x = np.concatenate((norm_result, onehot_result), axis=1)

    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=random_seed)
    class_weight = {0: 0.2, 1: 0.8}
    cls = VotingClassifier(estimators=[
        ('gbdt', GradientBoostingClassifier(n_estimators=1000, random_state=random_seed)),
        ('adaboost', AdaBoostClassifier(n_estimators=100, random_state=random_seed)),
        ('randomForest', RandomForestClassifier(n_estimators=100, class_weight=class_weight, random_state=random_seed)),
        ('xgboost', XGBClassifier(random_state=random_seed))
    ], voting='soft')
    #
    # voting_cls.fit(x_train, y_train)
    # # print(voting_cls.score(x_train, y_train))
    # print(voting_cls.score(x_test, y_test))
    # y_score = voting_cls.predict_proba(x_test)

    # cls = LogisticRegression(C=1, max_iter=10000,  class_weight=class_weight)
    # cls = SVC(C=30, probability=True, class_weight=class_weight)
    # cls = GradientBoostingClassifier(n_estimators=1000)
    # cls = AdaBoostClassifier(n_estimators=100)
    # cls = RandomForestClassifier(n_estimators=100, class_weight=class_weight)
    # cls = XGBClassifier(random_state=random_seed)
    cls.fit(x_train, y_train)
    print(cls.score(x_train, y_train))
    print(cls.score(x_test, y_test))
    y_score = cls.predict_proba(x_test)

    fpr, tpr, threshold = roc_curve(y_test, y_score[:, 1])
    roc_auc = auc(fpr, tpr)

    plot_roc_auc(fpr, tpr, roc_auc)

    predict_data = pd.read_csv('../data/predict.csv')
    # predict_data = predict_data[predict_data['brand'] == brand]
    predict_data['B8'] = 2020 - predict_data['B8']  # 将出生年份转为年龄
    x = predict_data.iloc[:, 2:-1]

    # norm_result = normalizer.transform(x[continuous_cols])
    # onehot_result = onehot_encoder.transform(x[discrete_cols])
    # x = np.concatenate((norm_result, onehot_result), axis=1)

    proba = cls.predict_proba(x)
    pred = cls.predict(x)
    print(pred)
    print(proba)

    # init_val = None
    # for col in ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8']:
    #     print(f"提升满意度: {col}")
    #     for inc_point in range(0, 6, 1):
    #         # print(f"提升满意度: {col}, 提升满意度点数: {inc_point}")
    #         data = x.copy()
    #         data[col] = data[col] + inc_point
    #         proba = cls.predict_proba(data)
    #         pred = cls.predict(data)
    #         if inc_point == 0:
    #             init_val = proba
    #         # print(f"预测结果: {proba[:, 1] - init_val[:, 1]}")
    #         print(proba[:, 1] - init_val[:, 1])
    #         print(f"pred: {pred}")


def analyze_pca():
    df = pd.read_csv(result_path)
    x = df.iloc[:, 1:-1]
    x = df[['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8']]
    y = df['buy']

    # continuous_cols = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'B2', 'B4', 'B8',
    #                    'B10', 'B13', 'B14', 'B15', 'B16', 'B17']
    # discrete_cols = ['B1', 'B3', 'B5', 'B6', 'B7', 'B9', 'B11', 'B12']
    #
    # normalizer = preprocessing.Normalizer()
    # norm_result = normalizer.fit_transform(x[continuous_cols])
    # onehot_encoder = OneHotEncoder(sparse=False)
    # onehot_result = onehot_encoder.fit_transform(x[discrete_cols])
    # x = np.concatenate((norm_result, onehot_result), axis=1)

    pca = PCA()
    pca.fit(x)
    components = pca.components_
    print(pca.explained_variance_ratio_)


if __name__ == '__main__':
    init_options()
    # preprocess()
    # analyze_satisfaction()
    # analyze_correlation()
    # analyze_discrete_point()
    # analyze_feature()
    # analyze_buy_rate()
    # analyze_pca()
    train()
